package nl.tue.bsdddump;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.NewCookie;



import no.catenda.peregrine.model.objects.json.IfdConcept;
import no.catenda.peregrine.model.objects.json.IfdName;

import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import org.codehaus.jackson.jaxrs.JacksonJsonProvider;

import com.db4o.Db4oEmbedded;
import com.db4o.ObjectContainer;
import com.db4o.ObjectSet;
import com.db4o.config.CommonConfiguration;
import com.db4o.config.EmbeddedConfiguration;
import com.db4o.ext.DatabaseClosedException;
import com.db4o.ext.DatabaseReadOnlyException;
import com.db4o.ext.Db4oIOException;
import com.db4o.query.Query;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.config.ClientConfig;
import com.sun.jersey.api.client.config.DefaultClientConfig;
import com.sun.jersey.api.json.JSONConfiguration;

public class PeregrineDump {
	private static final int MYTHREADS = 20;
	private static ArrayList<String> failedSearchTerms = new ArrayList<String>();
	private static Logger logger = Logger.getLogger(PeregrineDump.class);
	private static final String DB4OFILENAME = "model.db";
	private static HashSet<String> conceptGUIDS = new HashSet<String>();
	
	private static DBSearchCatalogue catalogue ;
	static ObjectContainer model_db;
	static boolean incremental = true;
	
	//static LeechRecord 
	/**
	 * @param args
	 * @throws TimeoutException 
	 * @throws Db4oIOException 
	 * @throws DatabaseReadOnlyException 
	 * @throws DatabaseClosedException 
	 */
	public static void main(String[] args) throws DatabaseClosedException, DatabaseReadOnlyException, Db4oIOException {
		
		Properties props = new Properties();
		
		configureDB();
				
		
		
		try {
			props.load(new FileInputStream("log4j.properties"));
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		PropertyConfigurator.configure(props);
		DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
		Calendar now = Calendar.getInstance();
        //System.out.println();
		logger.info("Search starting:"+dateFormat.format(now.getTime()));
		
		ClientConfig clientConfig = new DefaultClientConfig();
		clientConfig.getClasses().add(JacksonJsonProvider.class);
		clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
		Client client = Client.create(clientConfig);
		
		List<NewCookie> cookies = getCookies(client);
		ExecutorService executor = Executors.newFixedThreadPool(MYTHREADS);
		ExecutorService pool = Executors.newFixedThreadPool(MYTHREADS);
		
		 List<Future<ConceptSearchQueryResult>> set = new ArrayList<Future<ConceptSearchQueryResult>>();
		 int conceptCount=0;
		 int uniqueConceptCount=0;
		 
		 for(char ch1='q';ch1<='t';ch1++){
			 for(char ch2='a';ch2<='z';ch2++){
				 for(char ch3='a';ch3<='z';ch3++){

					 String search = Character.toString(ch1)+
							 Character.toString(ch2)+
							 Character.toString(ch3);
					 // System.out.println("searching "+search);   
					 System.out.println("test "+search);
					 if (catalogue.getSuccessfullQueries().contains(search)==false){
						 System.out.println("submit "+search);
						 @SuppressWarnings("unchecked")
						Callable<ConceptSearchQueryResult> callable = new PeregrineConceptSearchCallable(search,cookies);
						 Future<ConceptSearchQueryResult> future = pool.submit(callable);
						 set.add(future);
					 }


				 }    
			 }    
			 //pool.invokeAll(set);
			 //pool.invokeAll((Collection<? extends Callable<T>>) set);
			// pool.notifyAll();
		 }
		 //pool.notifyAll();
		 System.out.println("There are "+set.size()+" future tasks " );
		 int dbwriteCounter = 0;
		 for (Future<ConceptSearchQueryResult> future : set) {
			 //System.out.println("looking for "+((ConceptSearchQueryResult) future).getSearchTerm());
			 try {
				 dbwriteCounter++;
				 System.out.println(future.isDone());
				 ConceptSearchQueryResult searchResult =  future.get();
				 if (searchResult!=null){
					 //				 Iterator<ConceptSearchQueryResult> iterator = future.get().iterator();
					 //				 while (iterator.hasNext()) {
					 

					 //					 System.out.println(ifdConcept.getGuid()+" \t\t"+ifdConcept.getFullNames().get(0).getName());
					 // if (conceptGUIDS.contains(searchResult.getSearchTerm())==false){
					 //model_db.store(searchResult);
					// System.out.println("storeing search reult for "+searchResult.getSearchTerm());

					 catalogue.getSearchHistory().add(searchResult);
					 catalogue.getSuccessfullQueries().add(searchResult.getSearchTerm());
					 if (dbwriteCounter%100==0)
					 {
						// System.out.println("write to db");
						 model_db.store(catalogue);
						 model_db.commit();
						 
					 }
					 //System.out.println("done storing search result for "+searchResult.getSearchTerm());

					 uniqueConceptCount++;
					 //}

					 conceptCount++;
					 //				 	}

				 }
				 else 
				 {
					 System.out.println("chanceling "+future.get());
					 future.cancel(true);
				 }
			 } catch (InterruptedException e) {
				 // TODO Auto-generated catch block
				 e.printStackTrace();
				 model_db.commit();
			 } catch (ExecutionException e) {
				 future.cancel(true);
				 e.printStackTrace();
				 System.out.println("search failed for "+ e.getMessage()+"  \t"+e.getCause().getMessage());
				 failedSearchTerms.add(e.getMessage().substring(e.getMessage().lastIndexOf(":")+2));
				 catalogue.getSuccessfullQueries().add(e.getCause().getMessage());
				 model_db.store(catalogue);
				 model_db.commit();
			 }
		 } 
		 
		 for (String term:failedSearchTerms)
		 {
			 System.out.println(term);
			 logger.error("search failed for "+term);
		 }
		 now = Calendar.getInstance();
		 logger.info("Search ended "+dateFormat.format(now.getTime()));
		 logger.info("Search found "+conceptCount+" concepts, "+uniqueConceptCount+" unique");
		 Query query=model_db.query();
		 int storedConcepts=0;
			query.constrain(DBSearchCatalogue.class);
			ObjectSet result=query.execute();
			DBSearchCatalogue cat = (DBSearchCatalogue)result.get(0);
//			for (ConceptSearchQueryResult queries:cat.getSearchHistory())
//			{
//				storedConcepts = storedConcepts+queries.getSearchResult().size();
//			}
//			System.out.println(storedConcepts+" concpets in database");
		 model_db.commit();
		 model_db.close();
		 pool.shutdown();
	}
	
	
	
	private static void configureDB() {
		EmbeddedConfiguration db4oConfig = Db4oEmbedded.newConfiguration();
		db4oConfig.common().objectClass(IfdConcept.class).objectField("guid").indexed(true);
		db4oConfig.common().activationDepth(10);
		db4oConfig.common().updateDepth(10);
		
		
/*		File dbfile= new File(DB4OFILENAME);
		if (dbfile.exists())
		{
			model_db.close();
			
		}
*/		model_db = Db4oEmbedded.openFile(db4oConfig, DB4OFILENAME);
		Query query = model_db.query();
		query.constrain(DBSearchCatalogue.class);
		ObjectSet catalogues = query.execute();
		if (catalogues.size() > 0){
				catalogue = (DBSearchCatalogue) catalogues.get(0);
				System.out.println("loading existing catalogue from DB "+catalogue.getSearchHistory().size());
				logger.info("loading existing catalogue from DB"+catalogue.getSearchHistory().size());
				HashSet<String> successfullQueries = catalogue.getSuccessfullQueries();
				for (Iterator iterator = successfullQueries.iterator(); iterator.hasNext();) {
					String querystring= (String) iterator.next();
					System.out.println("catalouged "+ querystring);
					query=model_db.query();
					
				}
				
				for (Iterator iterator = catalogue.getSearchHistory().iterator(); iterator.hasNext();) {
					ConceptSearchQueryResult resultSet= (ConceptSearchQueryResult) iterator.next();
					if(resultSet.getSearchResult()!=null){
					HashSet<IfdConcept> searchResult = resultSet.getSearchResult();
					for (IfdConcept ifdConcept : searchResult) {
						List<IfdName> fullNames = ifdConcept.getFullNames();
						for (IfdName ifdName : fullNames) {
							System.out.print(ifdName.getName());
						}
						System.out.println("");
					}
					}
					
				}
		}
		else {
			logger.info("creating new catalogue from DB");
			catalogue = new DBSearchCatalogue();
			//model_db.store(catalogue);
			//model_db.commit();
		}
		
	}



	private static List<NewCookie> getCookies(Client client) {
		WebResource webResource = client
				.resource("http://bsddpilot.catenda.no/api/4.0/session/login?email=demo&password=demo");
		MultivaluedMap<String, String> formData = new com.sun.jersey.core.util.MultivaluedMapImpl();
		formData.add("email", "demo");
		formData.add("password", "demo");



		webResource.accept(MediaType.APPLICATION_JSON_TYPE);
		webResource.type(MediaType.APPLICATION_FORM_URLENCODED);

		ClientResponse response = webResource.type(MediaType.APPLICATION_FORM_URLENCODED).post(ClientResponse.class,formData);
		System.out.println(response.getStatus());
		List<NewCookie> cookies = response.getCookies();
		if (cookies != null && cookies.size() > 0) {
			for (NewCookie c : cookies) {
				System.out.println(c+"    ");
			}
		}
		return cookies;
	}
	
}
